﻿// ———————————————————————–
// <copyright file="AWS_KMLDal.cs" company="The MITRE Corporation">
//    Copyright (c) 2014 The MITRE Corporation. All rights reserved.
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
// </copyright>
// ———————————————————————–
/////////////////////////////////////////////////////////////////////////////////////////////////
// KMLDal.cs - Generated KML feeds from the ICNETSOA database
// Project: "EDXLSharp_Router"- "ICNETDAL"
//
// Language:    C#, .NET 4.0
// Platform:    Windows 7, VS 2013
// Author:      Brian Wilkins The MITRE Corporation
/////////////////////////////////////////////////////////////////////////////////////////////////

// Copyright (c) 2014 The MITRE Corporation. All rights reserved.
//
// NOTICE
// This software was produced for the U. S. Government
// under Contract No. FA8721-09-C-0001, and is
// subject to the Rights in Noncommercial Computer Software
// and Noncommercial Computer Software Documentation Clause
// (DFARS) 252.227-7014 (JUN 1995)using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using ICNET_KMLStyleEngine;
using ICNETServices;
using CoT_Library.Details;
using CoT_Library;
using System;
using EDXLSharp.EDXLDELib;
using GeoOASISWhereLib;
using EDXLSharp;
using EDXLCoT;
using EDXLSharp.EDXLHAVE_2_0Lib;
using EDXLSharp.CAPLib;
using MEXLSitRepLib;

namespace ICNETDAL
{
  /// <summary>
  /// Utility class to create KML placemarks for a DE object
  /// </summary>                       
  class AWS_KMLDal
  {
    #region Public Delegates/Events

    #endregion Public Delegates/Events

    #region Private Member Variables

    #endregion Private Member Variables

    #region Constructors

    #endregion Constructors

    #region Public Accessors

    #endregion Public Accessors

    #region Public Member Functions

    #endregion Public Member Functions

    #region Protected Member Functions

    #endregion Protected Member Functions

    #region Private Member Functions
    /// <summary>
    /// Creates KML based on the passed in information
    /// </summary>
    /// <param name="xmlWriter">XML Writer to hold KML xml</param>
    /// <param name="item">Data to create KML for</param>
    public static void GetKMLForDE(HashSet<ICNETStyle> usedStyles, XmlWriter xmlWriter, XElement item)
    {
      CAP capmsg;
      EDXLHAVE havemsg;
      SitRep sitRepmsg;
      CoTWrapper cotmsg;

      XNamespace gml = "http://www.opengis.net/gml";

      EDXLDE de = new EDXLDE(item.ToString(SaveOptions.None));

      foreach (ContentObject co in de.ContentObjects)
      {
        string dscString = de.DistributionID + de.SenderID + co.ContentDescription;
        byte[] b2 = System.Text.Encoding.ASCII.GetBytes(dscString);
        int DSCHash = ComputeHash(b2);

        if (co.XMLContent != null)
        {
          foreach (XElement doc in co.XMLContent.EmbeddedXMLContent)
          {
            string contentNS = doc.Name.NamespaceName;

            if (contentNS.Equals(EDXLConstants.CAP11Namespace) ||
              contentNS.ToString().Equals(EDXLConstants.CAP12Namespace))
            {
              capmsg = new CAP();
              XmlDocument xdoc = new XmlDocument();
              using (XmlReader xr = doc.CreateReader())
              {
                xdoc.Load(xr);
              }

              capmsg.ReadXML(xdoc.FirstChild);
            }
            else if (contentNS.Equals(EDXLConstants.HAVE10Namespace))
            {
              havemsg = new EDXLHAVE();
              XmlDocument xdoc = new XmlDocument();
              using (XmlReader xr = doc.CreateReader())
              {
                xdoc.Load(xr);
              }

              havemsg.ReadXML(xdoc.FirstChild);

            }
            else if (contentNS.Equals(EDXLConstants.SitRep10Namespace))
            {
              sitRepmsg = new SitRep();
              XmlDocument xdoc = new XmlDocument();
              using (XmlReader xr = doc.CreateReader())
              {
                xdoc.Load(xr);
              }

              sitRepmsg.ReadXML(xdoc.FirstChild);
              GetKMLForSitRep(usedStyles, xmlWriter, sitRepmsg, DSCHash, de);
            }
            else if (contentNS.Equals(EDXLConstants.EDXLCoTNamespace))
            {
              cotmsg = new CoTWrapper();
              XmlDocument xdoc = new XmlDocument();
              using (XmlReader xr = doc.CreateReader())
              {
                xdoc.Load(xr);
              }

              cotmsg.ReadXML(xdoc.FirstChild);
              GetKMLForCoT(usedStyles, xmlWriter, cotmsg, DSCHash, de);
            }
            else // handles all other xml types
            {
              ValueList contentKeywordVL = null;

              foreach (ValueList VL in co.ContentKeyword)
              {
                if (VL.ValueListURN.Equals(EDXLConstants.ContentKeywordListName))
                {
                  contentKeywordVL = VL;
                  break;
                }
              }

              if (contentKeywordVL != null)
              {
                OtherXMLWrapper xmlMsg = new OtherXMLWrapper(contentKeywordVL.Value, co.ContentDescription);
                XmlDocument xdoc = new XmlDocument();
                using (XmlReader xr = doc.CreateReader())
                {
                  xdoc.Load(xr);
                }

                xmlMsg.ReadXML(xdoc.FirstChild);
              }
            }
          }
        }
        else if (co.NonXMLContent != null)
        {
          GetKMLForNonXMLContent(usedStyles, xmlWriter, de, co, DSCHash);
        }
      }
    }

    /// <summary>
    /// Get KML for CoT message
    /// </summary>
    /// <param name="usedStyles">KML icon styles</param>
    /// <param name="xmlWriter">XML Writer to write KML to</param>
    /// <param name="cotmsg">CoT message</param>
    /// <param name="DSCHash">ICNET hash of CoT message</param>
    /// <param name="de">Parent DE</param>
    private static void GetKMLForCoT(HashSet<ICNETStyle> usedStyles, XmlWriter xmlWriter, CoTWrapper cotmsg, int DSCHash, EDXLDE de)
    {
      //SAMPLE CoT KML
      //<Placemark id="2134327899">
      //  <name>MITRE.DC998</name>
      //  <description>
      //    <![CDATA[FIRE DIVISION CHIEF<br/><br/>number: 703-595-9375<br/>battery: 15 %<br/>signal: -87 dBm<br/><a href="http://icnet-test.mitre.org/DataService.svc/HTML/2134327899">Details</a><br/>]]>
      //  </description>
      //  <styleUrl>#FIRE DIVISION CHIEF</styleUrl>
      //  <atom:author>
      //    <atom:name>dmcgarry@mitre.org</atom:name>
      //  </atom:author>
      //  <atom:link href="http://icnet-test.mitre.org/DataService.svc/HTML/2134327899" type="text/xml"/>
      //  <TimeSpan>
      //    <begin>2013-02-11T20:29:35Z</begin>
      //    <end>2013-02-11T20:59:35Z</end>
      //  </TimeSpan>
      //  <Snippet>FIRE DIVISION CHIEF</Snippet>
      //  <Point>
      //    <coordinates>-77.34475,38.98119</coordinates>
      //  </Point>
      //</Placemark>

      CotEvent evt = cotmsg.CoTEvent;

      xmlWriter.WriteStartElement("Placemark");
      xmlWriter.WriteAttributeString("id", DSCHash.ToString());

      string sTitle = GetTitleForCoT(evt);
      xmlWriter.WriteElementString("name", sTitle);
      xmlWriter.WriteStartElement("description");

      string styleID = string.Empty;
      string sFriendlyName = evt.Type;

      ICNETStyle style = KMLStyleEngine.GetStyleFromCoTType(evt.Type);

      if (style != null)
      {
        usedStyles.AddDependencies(style);
        styleID = @"#" + style.KMLStyleName;

        if (!String.IsNullOrWhiteSpace(style.KMLStyleFriendlyName))
        {
          sFriendlyName = style.KMLStyleFriendlyName;
        }
      }
      else
      {
        styleID = KMLStyleEngine.DefaultStyleId;
      }

      StringBuilder sbDescription = new StringBuilder(sFriendlyName + "<br/><br/>");

      GetKMLExtensionFromCoT(sbDescription, evt);

      Uri uriParams = new Uri(ICNETSOAConstants.ICNETdatalinkToHistoryDEHTML + "?Id=" + de.DistributionID + "&Sender=" + de.SenderID + "&DateTime=" + de.DateTimeSent.ToLocalTime().ToString());
      string sLink = uriParams.AbsoluteUri;

      sbDescription.Append("<a href=\"" + sLink + "\">Details</a><br/>");

      xmlWriter.WriteCData(sbDescription.ToString());
      xmlWriter.WriteEndElement(); //close description

      bool bEditable = false;
      bool bEditableFound = false;
      ICotDetailComponent details = cotmsg.CoTEvent.Detail.GetFirstElement("__icnet");

      if (details != null)
      {
        XmlNode node = details.XmlNode.SelectSingleNode("icall"); ;
        if (node != null)
        {
          bool bVal;
          bool bCanParse;

          bCanParse = Boolean.TryParse(node.Value, out bVal);
          if (bCanParse)
          {
            bEditable = bVal;
            bEditableFound = true;
          }
        }
      }

      if (bEditableFound)
      {
        xmlWriter.WriteStartElement("ExtendedData");
        xmlWriter.WriteAttributeString("xmlns", "icnet", string.Empty, "http://icnet.mitre.org/icall");
        string prefix = xmlWriter.LookupPrefix("http://icnet.mitre.org/icall");
        xmlWriter.WriteElementString(prefix, "editable", "http://icnet.mitre.org/icall", bEditable.ToString());
        xmlWriter.WriteEndElement(); //close ExtendedData
      }

      xmlWriter.WriteElementString("styleUrl", styleID);
      xmlWriter.WriteStartElement("atom", "author", "http://www.w3.org/2005/Atom");
      xmlWriter.WriteElementString("atom", "name", "http://www.w3.org/2005/Atom", de.SenderID);
      xmlWriter.WriteEndElement(); //close  atom:author

      xmlWriter.WriteStartElement("atom", "link", "http://www.w3.org/2005/Atom");
      xmlWriter.WriteAttributeString("href", sLink);
      xmlWriter.WriteAttributeString("type", "text/xml");
      xmlWriter.WriteEndElement(); //close atom:link

      xmlWriter.WriteStartElement("TimeSpan");
      string startTimeAsString = evt.Start.ToString("u");
      startTimeAsString = startTimeAsString.Replace(' ', 'T');
      xmlWriter.WriteElementString("begin", startTimeAsString);

      string endTimeAsString = evt.Stale.ToString("u");
      endTimeAsString = endTimeAsString.Replace(' ', 'T');
      xmlWriter.WriteElementString("end", endTimeAsString);
      xmlWriter.WriteEndElement(); //close timespan

      xmlWriter.WriteElementString("Snippet", sFriendlyName);

      xmlWriter.WriteStartElement("Point");
      xmlWriter.WriteStartElement("coordinates");
      xmlWriter.WriteString(evt.Point.Longitude.ToString() + "," + evt.Point.Latitude.ToString());
      xmlWriter.WriteEndElement(); //close coordinates
      xmlWriter.WriteEndElement(); //close point

      xmlWriter.WriteEndElement(); //close placemark
    }

    /// <summary>
    /// Get title from CoT message
    /// </summary>
    /// <param name="evt">CoT message</param>
    /// <returns>title</returns>
    private static string GetTitleForCoT(CotEvent evt)
    {
      string sTitle = "";

      ICotDetailComponent detailsUID = evt.Detail.GetFirstElement("uid");
      if (detailsUID != null && detailsUID.XmlNode != null)
      {
        XmlAttribute fvcotDetailsUID = detailsUID.XmlNode.Attributes["fvcot"];
        XmlAttribute icnetDetailsUID = detailsUID.XmlNode.Attributes["icnet"];

        if (fvcotDetailsUID != null)
        {
          sTitle = fvcotDetailsUID.Value;
        }
        else if (icnetDetailsUID != null)
        {
          sTitle = icnetDetailsUID.Value;
        }
        else
        {
          sTitle = evt.Uid;
        }
      }
      else
      {
        sTitle = evt.Uid;
      }

      return sTitle;
    }

    /// <summary>
    /// Get value from XML attribute
    /// </summary>
    /// <param name="node">XML</param>
    /// <param name="attributeName">attribute name</param>
    /// <returns>attribute value</returns>
    private static string GetValueFromAttribute(XmlNode node, string attributeName)
    {
      XmlAttribute attr = node.Attributes[attributeName];
      string val = "";

      if (attr != null)
      {
        val = attr.Value;
      }

      return val;
    }

    /// <summary>
    /// Get ICNET extension info in the CoT Message
    /// </summary>
    /// <param name="sbDescription">ICNET extension info</param>
    /// <param name="evt">CoT Message</param>
    private static void GetKMLExtensionFromCoT(StringBuilder sbDescription, CotEvent evt)
    {
      string sNumber = "";
      string sBattery = "";
      string sSignal = "";

      ICotDetailComponent details = evt.Detail.GetFirstElement("__icnet");
      if (details != null)
      {
        XmlNode node = details.XmlNode.SelectSingleNode("beacon");
        if (node != null)
        {
          sNumber = GetValueFromAttribute(node, "number");
          sBattery = GetValueFromAttribute(node, "battery");
          sSignal = GetValueFromAttribute(node, "signal");
        }

        string val = "";
        if (sNumber.Length > 0)
        {
          if (sNumber.Length == 10)
          {
            val = sNumber.Substring(0, 3) + "-" + sNumber.Substring(3, 3) + "-" + sNumber.Substring(6);
          }
          else if (sNumber.Length == 11)
          {
            val = sNumber.Substring(0, 1) + "-" + sNumber.Substring(1, 3) + "-" + sNumber.Substring(4, 3) + "-" + sNumber.Substring(7);
          }
          else if (sNumber.Length == 7)
          {
            val = sNumber.Substring(0, 3) + "-" + sNumber.Substring(3);
          }

          sbDescription.Append("number: " + val + "<br/>");
        }

        if (sBattery.Length > 0)
        {
          double dVal;
          bool bCanParse;

          bCanParse = Double.TryParse(sBattery, out dVal);
          if (bCanParse)
          {
            dVal = dVal * 100.0;
            val = dVal.ToString("##.#") + " %";
            sbDescription.Append("battery: " + val + "<br/>");
          }
        }

        if (sSignal.Length > 0)
        {
          val = sSignal + " dBm";
          sbDescription.Append("signal: " + val + "<br/>");
        }
      }
    }

    /// <summary>
    /// Create KML for non XML content
    /// </summary>
    /// <param name="usedStyles">KML icon style list</param>
    /// <param name="xmlWriter">XML Writer to write KML to</param>
    /// <param name="de">Parent DE</param>
    /// <param name="co">non XML content to convert</param>
    /// <param name="DSCHash">ICNET hash for non XML content</param>
    private static void GetKMLForNonXMLContent(HashSet<ICNETStyle> usedStyles, XmlWriter xmlWriter, EDXLDE de, ContentObject co, int DSCHash)
    {
      //<Placemark id="-1744337441">
      //  <name>SitRep Image 1[pfoxworthy@mitre.org,2907F810-C9BC-429F-922C-65B78FF1182A]</name>
      //  <description>
      //    <![CDATA[SitRep Image 1[pfoxworthy@mitre.org,2907F810-C9BC-429F-922C-65B78FF1182A]<br/><br/><img alt="Embedded Image" src="http://icnet-test.mitre.org/DataService.svc/MIME/-1744337441"/><a href="http://icnet-test.mitre.org/DataService.svc/MIME/-1744337441">Details</a><br/>]]></description>
      //  <styleUrl>#IMAGE</styleUrl>
      //  <atom:author>
      //    <atom:name>Admin</atom:name>
      //  </atom:author>
      //  <atom:link href="http://icnet-test.mitre.org/DataService.svc/MIME/-1744337441" type="text/xml"/>
      //  <TimeSpan>
      //    <end>2013-02-14T04:02:20Z</end>
      //  </TimeSpan>
      //  <Snippet>Sent by: </Snippet>
      //  <Point>
      //    <coordinates>-77.0326,38.9044</coordinates>
      //  </Point>
      //</Placemark>

      xmlWriter.WriteStartElement("Placemark");
      xmlWriter.WriteAttributeString("id", DSCHash.ToString());

      xmlWriter.WriteElementString("name", co.ContentDescription);
      xmlWriter.WriteStartElement("description");

      string styleID = string.Empty;
      string sFriendlyName = "Image"; //TODO: clean up this HACK

      ICNETStyle style = KMLStyleEngine.GetStyleFromContentOrTitle("image", "image");

      if (style != null)
      {
        usedStyles.AddDependencies(style);
        styleID = @"#" + style.KMLStyleName;

        if (!String.IsNullOrWhiteSpace(style.KMLStyleFriendlyName))
        {
          sFriendlyName = style.KMLStyleFriendlyName;
        }
      }
      else
      {
        styleID = KMLStyleEngine.DefaultStyleId;
      }

      Uri uriParams = new Uri(ICNETSOAConstants.ICNETdatalinkToHistoryDEHTML + "?Id=" + de.DistributionID + "&Sender=" + de.SenderID + "&DateTime=" + de.DateTimeSent.ToLocalTime().ToString());
      string sLink = uriParams.AbsoluteUri;

      uriParams = new Uri(ICNETSOAConstants.ICNETdatalinkToHistoryMIME + "?Id=" + de.DistributionID + "&Sender=" + de.SenderID + "&DateTime=" + de.DateTimeSent.ToLocalTime().ToString() + "&ContentDescription=" + co.ContentDescription);
      string sMIMELink = uriParams.AbsoluteUri;

      StringBuilder sbDescription = new StringBuilder("<img alt=\"Embedded Image\" src=\"" + sMIMELink + "\"/><br/><br/>");
      sbDescription.Append("<a href=\"" + sLink + "\">Details</a><br/>");

      xmlWriter.WriteCData(sbDescription.ToString());
      xmlWriter.WriteEndElement(); //close description

      xmlWriter.WriteElementString("styleUrl", styleID);
      xmlWriter.WriteStartElement("atom", "author", "http://www.w3.org/2005/Atom");
      xmlWriter.WriteElementString("atom", "name", "http://www.w3.org/2005/Atom", de.SenderID);
      xmlWriter.WriteEndElement(); //close  atom:author

      xmlWriter.WriteStartElement("atom", "link", "http://www.w3.org/2005/Atom");
      xmlWriter.WriteAttributeString("href", sLink);
      xmlWriter.WriteAttributeString("type", "text/xml");
      xmlWriter.WriteEndElement(); //close atom:link

      xmlWriter.WriteStartElement("TimeSpan");
      string startTimeAsString = de.DateTimeSent.ToString("u");
      startTimeAsString = startTimeAsString.Replace(' ', 'T');
      xmlWriter.WriteElementString("begin", startTimeAsString);

      string endTimeAsString = de.DateTimeSent.AddDays(1.0).ToString("u");
      endTimeAsString = endTimeAsString.Replace(' ', 'T');
      xmlWriter.WriteElementString("end", endTimeAsString);
      xmlWriter.WriteEndElement(); //close timespan

      xmlWriter.WriteElementString("Snippet", "Sent by: " + de.SenderID);

      xmlWriter.WriteStartElement("Point");
      xmlWriter.WriteStartElement("coordinates");
      string point = de.TargetArea[0].Circle[0];

      if (point != null)
      {
        string latlon = point.Split(' ')[0];
        string[] coords = latlon.Split(',');
        int lonIndex = 1;
        int latIndex = 0;
        xmlWriter.WriteString(coords[lonIndex] + "," + coords[latIndex]); //OASIS GML has point lat and lon backwards to normal GML standard 
      }

      xmlWriter.WriteEndElement(); //close coordinates
      xmlWriter.WriteEndElement(); //close point

      xmlWriter.WriteEndElement(); //close placemark    
    }

    /// <summary>
    /// Create KML for SitRep message
    /// </summary>
    /// <param name="usedStyles">KML icon style list</param>
    /// <param name="xmlWriter">XML Writer to write KML to</param>
    /// <param name="sitRepmsg">SitRep message to convert</param>
    /// <param name="DSCHash">ICNET hash for SitRep message</param>
    /// <param name="de">Parent DE</param>
    private static void GetKMLForSitRep(HashSet<ICNETStyle> usedStyles, XmlWriter xmlWriter, SitRep sitRepmsg, int DSCHash, EDXLDE de)
    {
      //<Placemark id="903105412">
      //  <name>Field Observation - GeneralObservation </name>
      //  <description>
      //    <![CDATA[Observation: Sender's Phone Number: 2024655739  First post by PRONET at VTA
      //    <a href="http://icnet-test.mitre.org/DataService.svc/HTML/903105412">Details</a><br/>]]>
      //  </description>
      //  <styleUrl>#SITREP GENERAL</styleUrl>
      //  <atom:author>
      //    <atom:name>pfoxworthy@mitre.org</atom:name>
      //  </atom:author>
      //  <atom:link href="http://icnet-test.mitre.org/DataService.svc/HTML/903105412" type="text/xml"/>
      //  <TimeSpan>
      //    <end>2013-02-14T16:02:20Z</end>
      //  </TimeSpan>
      //  <Snippet>MessageID: 2907F810-C9BC-429F-922C-65B78FF1182A</Snippet>
      //  <Point>
      //    <coordinates>-77.0326,38.9044</coordinates>
      //  </Point>
      //</Placemark>
      xmlWriter.WriteStartElement("Placemark");
      xmlWriter.WriteAttributeString("id", DSCHash.ToString());

      string sTitle = GetTitleForSitRep(sitRepmsg);
      xmlWriter.WriteElementString("name", sTitle);
      xmlWriter.WriteStartElement("description");

      string styleID = string.Empty;
      string sFriendlyName = "Field Observation"; //TODO: clean up this HACK

      ICNETStyle style = KMLStyleEngine.GetStyleFromContentOrTitle(sFriendlyName, sTitle);

      if (style != null)
      {
        usedStyles.AddDependencies(style);
        styleID = @"#" + style.KMLStyleName;

        if (!String.IsNullOrWhiteSpace(style.KMLStyleFriendlyName))
        {
          sFriendlyName = style.KMLStyleFriendlyName;
        }
      }
      else
      {
        styleID = KMLStyleEngine.DefaultStyleId;
      }

      StringBuilder sbDescription = new StringBuilder("Observation - " + GetReportTextForSitRep(sitRepmsg) + "<br/><br/>");

      Uri uriParams = new Uri(ICNETSOAConstants.ICNETdatalinkToHistoryDEHTML + "?Id=" + de.DistributionID + "&Sender=" + de.SenderID + "&DateTime=" + de.DateTimeSent.ToLocalTime().ToString());
      string sLink = uriParams.AbsoluteUri;

      sbDescription.Append("<a href=\"" + sLink + "\">Details</a><br/>");

      xmlWriter.WriteCData(sbDescription.ToString());
      xmlWriter.WriteEndElement(); //close description

      xmlWriter.WriteElementString("styleUrl", styleID);
      xmlWriter.WriteStartElement("atom", "author", "http://www.w3.org/2005/Atom");
      xmlWriter.WriteElementString("atom", "name", "http://www.w3.org/2005/Atom", de.SenderID);
      xmlWriter.WriteEndElement(); //close  atom:author

      xmlWriter.WriteStartElement("atom", "link", "http://www.w3.org/2005/Atom");
      xmlWriter.WriteAttributeString("href", sLink);
      xmlWriter.WriteAttributeString("type", "text/xml");
      xmlWriter.WriteEndElement(); //close atom:link

      xmlWriter.WriteStartElement("TimeSpan");
      string startTimeAsString = sitRepmsg.FromDateTime.ToString("u");
      startTimeAsString = startTimeAsString.Replace(' ', 'T');
      xmlWriter.WriteElementString("begin", startTimeAsString);

      string endTimeAsString = sitRepmsg.ToDateTime.ToString("u");
      endTimeAsString = endTimeAsString.Replace(' ', 'T');
      xmlWriter.WriteElementString("end", endTimeAsString);
      xmlWriter.WriteEndElement(); //close timespan

      xmlWriter.WriteElementString("Snippet", "Sent by: " + de.SenderID);

      xmlWriter.WriteStartElement("Point");
      xmlWriter.WriteStartElement("coordinates");
      GMLPoint gmlPoint = sitRepmsg.ReportLocation.Location as GMLPoint;

      if (gmlPoint != null)
      {
        string latlon = gmlPoint.Pos.ToString();
        string[] coords = latlon.Split(' ');
        int lonIndex = 1;
        int latIndex = 0;
        xmlWriter.WriteString(coords[lonIndex] + "," + coords[latIndex]); //OASIS GML has point lat and lon backwards to normal GML standard 
      }

      xmlWriter.WriteEndElement(); //close coordinates
      xmlWriter.WriteEndElement(); //close point

      xmlWriter.WriteEndElement(); //close placemark
    }

    /// <summary>
    /// Get observation text from SitRep message
    /// </summary>
    /// <param name="sitRepmsg">SitRep message</param>
    /// <returns>Observation text</returns>
    private static string GetReportTextForSitRep(SitRep sitRepmsg)
    {
      string sText = "None Entered";

      if (sitRepmsg.MessageReportType.HasValue)
      {
        if (sitRepmsg.MessageReportType == SitRepReportType.FieldObservation)
        {
          SituationObservation sitob = sitRepmsg.Report as SituationObservation;

          if (sitob != null)
          {
            sText = sitob.ObservationText;
          }
        }
        //TODO: support other message report types
      }

      return sText;
    }

    /// <summary>
    /// Get title from SitRep message
    /// </summary>
    /// <param name="sitRepmsg">SitRep message</param>
    /// <returns>SitRep title</returns>
    private static string GetTitleForSitRep(SitRep sitRepmsg)
    {
      string sTitle = "Field Observation";

      if (sitRepmsg.MessageReportType.HasValue)
      {
        if (sitRepmsg.MessageReportType == SitRepReportType.FieldObservation)
        {
          SituationObservation sitob = sitRepmsg.Report as SituationObservation;

          if (sitob != null)
          {
            if (sitob.ObservationType.HasValue)
            {
              sTitle = "Field Observation - " + sitob.ObservationType.ToString();
            }
          }
        }
        //TODO: support other message report types
      }

      return sTitle;
    }

    /// <summary>
    /// Get report type from SitRep message
    /// </summary>
    /// <param name="sitRepmsg">SitRep message</param>
    /// <returns>SitRep report type</returns>
    private static string GetTypeForSitRep(SitRep sitRepmsg)
    {
      string sType = "GeneralObservation";

      if (sitRepmsg.MessageReportType.HasValue)
      {
        if (sitRepmsg.MessageReportType == SitRepReportType.FieldObservation)
        {
          SituationObservation sitob = sitRepmsg.Report as SituationObservation;

          if (sitob != null)
          {
            if (sitob.ObservationType.HasValue)
            {
              sType = sitob.ObservationType.ToString();
            }
          }
        }
        //TODO: support other message report types
      }

      return sType;
    }

    /// <summary>
    /// Get KML for playback from non XML content
    /// </summary>
    /// <param name="usedStyles">KML icon styles</param>
    /// <param name="xmlWriter">XML Writer to write KML to</param>
    /// <param name="de">DE parent</param>
    /// <param name="co">non XML content</param>
    /// <param name="DSCHash">ICNET hash of non XML content</param>
    /// <returns>description of non XML content</returns>
    private static string GetPlaybackKMLForNonXMLContent(HashSet<ICNETStyle> usedStyles, XmlWriter xmlWriter, EDXLDE de, ContentObject co, int DSCHash)
    {
      //SAMPLE CoT KML
      //<Placemark>
      //  <name>Image - MITRE.dmcgarry</name>
      //  <TimeStamp>
      //    <when>2005-08-21T09:01:00Z</when>
      //  </TimeStamp>
      //  <styleUrl>#RECON TEAM</styleUrl>
      //  <Point>
      //    <coordinates>55.374000,-4.640000</coordinates>
      //  </Point>
      //</Placemark

      string styleID = string.Empty;
      string sFriendlyName = "Image"; //TODO: clean up this HACK

      ICNETStyle style = KMLStyleEngine.GetStyleFromContentOrTitle("image", "image");

      if (style != null)
      {
        usedStyles.AddDependencies(style);
        styleID = @"#" + style.KMLStyleName;

        if (!String.IsNullOrWhiteSpace(style.KMLStyleFriendlyName))
        {
          sFriendlyName = style.KMLStyleFriendlyName;
        }
      }
      else
      {
        styleID = KMLStyleEngine.DefaultStyleId;
      }

      xmlWriter.WriteStartElement("Placemark");
      string sender = de.SenderID.Substring(0, de.SenderID.IndexOf("@"));

      xmlWriter.WriteElementString("name", "Image - " + sender);
      xmlWriter.WriteElementString("snippet", "");

      xmlWriter.WriteStartElement("description");

      Uri uriParams = new Uri(ICNETSOAConstants.ICNETdatalinkToHistoryMIME + "?Id=" + de.DistributionID + "&Sender=" + de.SenderID + "&DateTime=" + de.DateTimeSent.ToLocalTime().ToString() + "&ContentDescription=" + co.ContentDescription);
      string sMIMELink = uriParams.AbsoluteUri;

      xmlWriter.WriteCData("<img alt=\"Embedded Image\" src=\"" + sMIMELink + "\"/><br/>");
      xmlWriter.WriteEndElement(); //close description

      xmlWriter.WriteStartElement("TimeStamp");
      string zuluTimeAsString = de.DateTimeSent.ToString("u");
      zuluTimeAsString = zuluTimeAsString.Replace(' ', 'T');
      xmlWriter.WriteElementString("when", zuluTimeAsString);
      xmlWriter.WriteEndElement(); //close TimeStamp

      xmlWriter.WriteElementString("styleUrl", styleID);
      xmlWriter.WriteStartElement("Point");
      xmlWriter.WriteStartElement("coordinates");
      string point = de.TargetArea[0].Circle[0];

      if (point != null)
      {
        string latlon = point.Split(' ')[0];
        string[] coords = latlon.Split(',');
        int lonIndex = 1;
        int latIndex = 0;
        xmlWriter.WriteString(coords[lonIndex] + "," + coords[latIndex]); //OASIS GML has point lat and lon backwards to normal GML standard 
      }

      xmlWriter.WriteEndElement(); //close coordinates
      xmlWriter.WriteEndElement(); //close point

      xmlWriter.WriteEndElement(); //close placemark

      return sFriendlyName;
    }

    /// <summary>
    /// Get KML for playback from SitRep message
    /// </summary>
    /// <param name="usedStyles">KML icon styles</param>
    /// <param name="xmlWriter">XML Writer to write KML to</param>
    /// <param name="de">DE parent</param>
    /// <param name="sitRepMsg">SitRep message</param>
    /// <param name="DSCHash">ICNET hash of SitRep message</param>
    /// <returns>description of SitRep message</returns>
    private static string GetPlaybackKMLForSitRep(HashSet<ICNETStyle> usedStyles, XmlWriter xmlWriter, SitRep sitRepMsg, int DSCHash, EDXLDE de)
    {
      //SAMPLE CoT KML
      //<Placemark>
      //  <name>FieldObservation - MITRE.dmcgarry</name>
      //  <TimeStamp>
      //    <when>2005-08-21T09:01:00Z</when>
      //  </TimeStamp>
      //  <styleUrl>#RECON TEAM</styleUrl>
      //  <Point>
      //    <coordinates>55.374000,-4.640000</coordinates>
      //  </Point>
      //</Placemark>

      xmlWriter.WriteStartElement("Placemark");

      string styleID = string.Empty;
      string sFriendlyName = "Field Observation"; //TODO: clean up this HACK

      string sTitle = GetTitleForSitRep(sitRepMsg);
      ICNETStyle style = KMLStyleEngine.GetStyleFromContentOrTitle(sFriendlyName, sTitle);

      if (style != null)
      {
        usedStyles.AddDependencies(style);
        styleID = @"#" + style.KMLStyleName;

        if (!String.IsNullOrWhiteSpace(style.KMLStyleFriendlyName))
        {
          sFriendlyName = style.KMLStyleFriendlyName;
        }
      }
      else
      {
        styleID = KMLStyleEngine.DefaultStyleId;
      }
      string sender = de.SenderID.Substring(0, de.SenderID.IndexOf("@"));

      xmlWriter.WriteElementString("name", GetTypeForSitRep(sitRepMsg) + " - " + sender);
      xmlWriter.WriteElementString("snippet", "");

      xmlWriter.WriteStartElement("description");
      StringBuilder sbDescription = new StringBuilder(GetReportTextForSitRep(sitRepMsg) + "<br/><br/>");

      Uri uriParams = new Uri(ICNETSOAConstants.ICNETdatalinkToHistoryDEHTML + "?Id=" + de.DistributionID + "&Sender=" + de.SenderID + "&DateTime=" + de.DateTimeSent.ToLocalTime().ToString());
      string sLink = uriParams.AbsoluteUri;

      sbDescription.Append("<a href=\"" + sLink + "\">Details</a><br/>");

      xmlWriter.WriteCData(sbDescription.ToString());
      xmlWriter.WriteEndElement(); //close description

      xmlWriter.WriteStartElement("TimeStamp");
      string zuluTimeAsString = de.DateTimeSent.ToString("u");
      zuluTimeAsString = zuluTimeAsString.Replace(' ', 'T');
      xmlWriter.WriteElementString("when", zuluTimeAsString);
      xmlWriter.WriteEndElement(); //close TimeStamp

      xmlWriter.WriteElementString("styleUrl", styleID);
      xmlWriter.WriteStartElement("Point");
      xmlWriter.WriteStartElement("coordinates");
      GMLPoint gmlPoint = sitRepMsg.ReportLocation.Location as GMLPoint;

      if (gmlPoint != null)
      {
        string latlon = gmlPoint.Pos.ToString();
        string[] coords = latlon.Split(' ');
        int lonIndex = 1;
        int latIndex = 0;
        xmlWriter.WriteString(coords[lonIndex] + "," + coords[latIndex]); //OASIS GML has point lat and lon backwards to normal GML standard 
      }

      xmlWriter.WriteEndElement(); //close coordinates
      xmlWriter.WriteEndElement(); //close point

      xmlWriter.WriteEndElement(); //close placemark

      return sFriendlyName;
    }

    /// <summary>
    /// Create hash for supplied data
    /// </summary>
    /// <param name="data">Data to hash</param>
    /// <returns>Hash Int</returns>
    private static int ComputeHash(byte[] data)
    {
      unchecked
      {
        const int p = 16777619;
        int hash = (int)2166136261;

        for (int i = 0; i < data.Length; i++)
          hash = (hash ^ data[i]) * p;

        hash += hash << 13;
        hash ^= hash >> 7;
        hash += hash << 3;
        hash ^= hash >> 17;
        hash += hash << 5;
        return hash;
      }
    }
    #endregion Private Member Functions
                
  }
}
